#include "pp_utils.h"

#include <sstream>

// c++17
#if __cplusplus >= 201703L
#include <filesystem>
namespace fs = std::filesystem;
#else
#include "pp_file.h"
namespace fs = pp;
#endif

namespace pp
{
    std::string Trim(const std::string& s)
    {
        if (s.empty()) return s;

        const char* sz = s.data();
        int start = 0;
        int end = s.length() - 1;

        for (start = 0; start < s.length(); start++)
        {
            if (!isspace(sz[start])) break;
        }

        if (start < end)
        {
            for (end = s.length() - 1; end >= 0; end--)
            {
                if (!isspace(sz[end])) break;
            }
        }

        return s.substr(start, end - start + 1);
    }

    std::string TrimLeft(const std::string& s, const std::string& p)
    {
        if (s.empty()) return s;

        if (s.size() <= p.size() || p.empty()) return s;

        size_t fpos = s.find(p);
        if (fpos == std::string::npos) return s;

        return s.substr(fpos + p.length());
    }

    std::string TrimRight(const std::string& s, const std::string& p)
    {
        if (s.empty()) return s;

        if (s.size() <= p.size() || p.empty()) return s;

        size_t fpos = s.find_last_of(p);
        if (fpos == std::string::npos) return s;

        return s.substr(0, fpos - p.length() + 1);
    }

    void Split(const std::string& s, const std::string& sp, std::vector<std::string>& slist)
    {
        if (s.empty()) return;

        if (s.size() <= sp.size() || sp.empty())
        {
            slist.push_back(s);
            return;
        }

        size_t pos = 0;
        while (true)
        {
            size_t fpos = s.find(sp, pos);

            if (fpos == std::string::npos)
            {
                slist.push_back(s.substr(pos));
                break;
            }
            slist.push_back(s.substr(pos, fpos - pos));
            pos = fpos + sp.length();
        }
    }

    std::string GetFileName(const std::string& path)
    {
        // 注意这里的i不能定义为size_t，很可能导致i=0后再减去1时越界
        for (int i = path.length() - 1; i >= 0; i--)
        {
            if (path.at(i) == '/' || path.at(i) == '\\')
            {
                return path.substr(i + 1);
            }
        }
        return path;
    }

    std::string GetFileExtension(const std::string& path)
    {
        // 注意这里的i不能定义为size_t，很可能导致i=0后再减去1时越界
        for (int i = path.length() - 1; i >= 0; i--)
        {
            if (path.at(i) == '.')
            {
                return path.substr(i);
            }
        }
        return "";
    }

    // 去掉当前目录，当前目录以./或.\\开头
    std::string TrimPath(const std::string& path)
    {
        if (path.empty() || path.at(0) != '.') return path;
        size_t i = 1;

        for (i = 1; i < path.length(); i++)
        {
            if (path.at(i) != '/' && path.at(i) != '\\') break;
        }
        return path.substr(i);
    }

    std::string GetRelativePath(const std::string& path, const std::string& file)
    {
        size_t pos = file.find_first_of(path);
        std::string rel = pos == std::string::npos ? file : file.substr(pos + path.size());

        for (int i = rel.length() - 1; i >= 0; i--)
        {
            if (rel.at(i) == '/' || rel.at(i) == '\\')
            {
                return rel.substr(0, i);
            }
        }

        return rel;
    }

    bool CreateDirectories(const std::string& path)
    {
        return fs::create_directories(path);
    }

    // 字段名字转驼峰命名法
    std::string ToPascalCase(const std::string& fname)
    {
        uint8_t state = 0;

        std::stringstream result;
        for (size_t i = 0; i < fname.length(); i++)
        {
            char ch = fname.at(i);
            if (state == 0)
            {
                if (ch == '_' || ch == '-')
                {
                    state = 1;
                }
                else
                {
                    result << (char)(i == 0 ? std::toupper(ch) : ch);
                }
            }
            else if (state == 1)
            {
                if (ch != '_' && ch != '_')
                {
                    result << (char)std::toupper(ch);
                    state = 0;
                }
            }
        }
        return result.str();
    }

    // 字段名字转c语言命名法 (fname中出现大写字母会转换为小写，并在前面添加_)
    std::string ToCCase(const std::string& fname)
    {
        std::stringstream result;
        for (size_t i = 0; i < fname.length(); i++)
        {
            char ch = fname.at(i);
            if (ch >= 'A' && ch <= 'Z')
            {
                char u = std::tolower(ch);
                if (i > 0) result << "_";
                result << u;
            }
            else if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
            {
                result << ch;
            }
            else
            {
                result << "_";
            }
        }
        return result.str();
    }

    // 名字转换为小写
    std::string ToLower(const std::string& fname)
    {
        std::stringstream result;
        for (size_t i = 0; i < fname.length(); i++)
        {
            char ch = fname.at(i);
            if (ch >= 'A' && ch <= 'Z')
            {
                char u = std::tolower(ch);
                result << u;
            }
            else
            {
                result << ch;
            }
        }
        return result.str();
    }

    // 查找替换所有
    std::string Replace(const std::string& s, const std::string& fs, const std::string& rs)
    {
        if (s.empty()) return s;

        if (s.size() <= fs.size() || fs.empty())
        {
            return s;
        }

        size_t pos = 0;
        std::stringstream ss;
        while (true)
        {
            size_t fpos = s.find(fs, pos);

            if (fpos == std::string::npos)
            {
                ss << s.substr(pos);
                break;
            }
            ss << s.substr(pos, fpos - pos) << rs;
            pos = fpos + fs.length();
        }

        return ss.str();
    }

    // 第一个字母小写
    std::string ToLowerFirst(const std::string& fname)
    {
        if (fname.empty()) return fname;

        std::stringstream result;
        char ch = std::tolower(fname.at(0));

        result << ch << fname.substr(1);

        return result.str();
    }
};
